package robocup.component.infotypes;

import java.util.ArrayList;

/**
 * This class represents a collection of all seen objects of a certain cycle for
 * one player. These objects can be flags, lines, players and the ball. For each
 * of these types, a predefined list (buffers) of possible objects (except for
 * the ball, which is unique) exists, were the corresponding elements can be
 * taken from, and added to the lists of seen items (for saving performance).
 */
public class VisualInfo
    extends Info {

  /**
   * A buffer for seeable flags.
   */
  private final ArrayList<FlagInfo>   flagBuffer   = new ArrayList<FlagInfo>();

  /**
   * A buffer for seeable lines.
   */
  private final ArrayList<LineInfo>   lineBuffer   = new ArrayList<LineInfo>();

  /**
   * A buffer for seeable players.
   */
  private final ArrayList<PlayerInfo> playerBuffer = new ArrayList<PlayerInfo>();

  /**
   * The ball-buffer
   */
  private final BallInfo              ballBuffer   = new BallInfo(0, 0.0, 0.0, 0.0,
                                                 0.0);

  /**
   * The flags seen in a certain cycle.
   */
  private final ArrayList<FlagInfo>   flags        = new ArrayList<FlagInfo>();

  /**
   * The lines seen in a certain cycle.
   */
  private final ArrayList<LineInfo>   lines        = new ArrayList<LineInfo>();

  /**
   * The players seen in a certain cycle.
   */
  private final ArrayList<PlayerInfo> players      = new ArrayList<PlayerInfo>();

  /**
   * The seen ball (null, if not seen).
   */
  private BallInfo              ball         = null;

  /**
   * A collection of all visual things perceived in one cycle. Buffers are
   * initialized (50 flags, 4 lines, 21 players and 1 ball). <br>
   * <br>
   * 
   * @param cycle -
   *          the time, when the objects where seen
   */
  public VisualInfo(
      final int cycle) {

    super(cycle);
    // fill the buffer
    for (int i = 0; i < 50; i++) {
      this.flagBuffer.add(new FlagInfo(0, 0, 0, 0));
    }
    for (int i = 0; i < 4; i++) {
      this.lineBuffer.add(new LineInfo(0, 0, 0, 0));
    }
    for (int i = 0; i < 21; i++) {
      this.playerBuffer.add(new PlayerInfo(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false,
          false));
    }
    this.ball = null;

    this.infoType = Info.TYPE_VISUAL;
  }

  /**
   * This method adds all seen elements back to the buffer lists and removes
   * them from seen lists
   */
  public void reset() {

    for (final int i = 0; i < this.lines.size();) {
      this.lineBuffer.add(this.lines.remove(0));
    }

    for (final int i = 0; i < this.flags.size();) {
      this.flagBuffer.add(this.flags.remove(0));
    }

    for (final int i = 0; i < this.players.size();) {
      this.playerBuffer.add(this.players.remove(0));
    }

    this.ball = null;
  }

  /**
   * Method that takes a Flag from Buffer and adds it with setted parameters to
   * the perceived flags.
   * 
   * @param cycle -
   *          the cycle, when the flag was seen
   * @param id -
   *          the flags id
   * @param dDist -
   *          the diatsnce to the flag
   * @param dDir -
   *          the direction to the flag
   */
  public void addFlag(final int cycle,
      final int id,
      final double dDist,
      final double dDir) {

    FlagInfo flag;
    if (this.flagBuffer.size() != 0) {
      flag = this.flagBuffer.remove(0);
      flag.setCycle(cycle);
      flag.setId(id);
      flag.setDistance(dDist);
      flag.setDirection(dDir);
    }
    else {
      flag = new FlagInfo(cycle, id, dDist, dDir);
    }
    this.flags.add(flag);
  }

  /**
   * This method takes a line from buffer, sets all values and adds it to the
   * list of seen lines.
   * 
   * @param cycle -
   *          the cycle, when the line was seen
   * @param id -
   *          the id of the line
   * @param dDist -
   *          the distance to the line
   * @param dDir -
   *          the direction to the line
   */
  public void addLine(final int cycle,
      final int id,
      final double dDist,
      final double dDir) {

    final LineInfo line = this.lineBuffer.remove(0);
    line.setCycle(cycle);
    line.setId(id);
    line.setDistance(dDist);
    line.setDirection(dDir);
    this.lines.add(line);
  }

  /**
   * This method takes al player from the buffer, sets all given parameters and
   * adds it to the list of seen players
   * 
   * @param cycle -
   *          the cycle, in which the player was seen
   * @param type -
   *          either enemy, friend or unknown
   * @param iNum -
   *          the tricot number
   * @param dDist -
   *          distance
   * @param dDir -
   *          direction
   * @param dDistChange -
   *          change-Distance
   * @param dDirChange -
   *          change Direction
   * @param dBodyFacingDir -
   *          Body Direction
   * @param dHeadFacingDir -
   *          Head Direction
   * @param dPointDir -
   *          pointing to
   * @param tackle -
   *          is tackling
   * @param goalie -
   *          is goalie
   */
  public void addPlayer(final int cycle,
      final int type,
      final int iNum,
      final double dDist,
      final double dDir,
      final double dDistChange,
      final double dDirChange,
      final double dBodyFacingDir,
      final double dHeadFacingDir,
      final double dPointDir,
      final boolean tackle,
      final boolean goalie,
      final String teamName) {

    final PlayerInfo player = this.playerBuffer.remove(0);
    player.setCycle(cycle);
    player.setType(type);
    player.setId(iNum);
    player.setDistance(dDist);
    player.setDirection(dDir);
    player.setDistChange(dDistChange);
    player.setDirChange(dDirChange);
    player.setBodyDir(dBodyFacingDir);
    player.setHeadDir(dHeadFacingDir);
    player.setPointDir(dPointDir);
    player.setTackle(tackle);
    player.setGoalie(goalie);
    player.setTeamName(teamName);

    this.players.add(player);
  }

  /**
   * This method sets the ball as seen and updates the corresponding values.
   * 
   * @param cycle -
   *          when the ball was seen
   * @param dDist -
   *          the distance to the ball
   * @param dDir -
   *          the direction to the ball
   * @param dDistChange -
   *          change in distance of the ball (speed indicator)
   * @param dDirChange -
   *          change in direction of the ball (speed indicator)
   */
  public void setBall(final int cycle,
      final double dDist,
      final double dDir,
      final double dDistChange,
      final double dDirChange) {

    if (this.cycle == 0) {
      this.cycle = cycle;
    }
    this.ballBuffer.setCycle(cycle);
    this.ballBuffer.setDistance(dDist);
    this.ballBuffer.setDirection(dDir);
    this.ballBuffer.setDistChange(dDistChange);
    this.ballBuffer.setDirChange(dDirChange);

    this.ball = this.ballBuffer;
  }

  /**
   * @return Returns all seen flags of a certain cycle.
   */
  public FlagInfo[] getFlags() {

    return (this.flags.toArray(new FlagInfo[this.flags.size()]));
  }

  /**
   * @return Returns all seen lines of a certain cycle.
   */
  public LineInfo[] getLines() {

    return (this.lines.toArray(new LineInfo[this.lines.size()]));
  }

  /**
   * @return Returns all seen players of a certain cycle.
   */
  public PlayerInfo[] getPlayers() {

    // return (PlayerInfo[])players.toArray();
    return (this.players.toArray(new PlayerInfo[this.players.size()]));
  }

  /**
   * Returns the ball. This is <code>null</code>, if not seen in the given
   * cycle.
   * 
   * @return - the ball.
   */
  public BallInfo getBall() {

    return this.ball;
  }

  /**
   * Checks, whether nothing was seen. This is the case, if the corresponding
   * lists for flags, lines and players are empty, and the ball-reference is
   * null.
   * 
   * @return - true, if no object was seen.
   */
  public boolean isEmpty() {

    return (this.flags.size() + this.lines.size() + this.players.size() == 0 && this.ball == null);
  }

}
